<?php

/**
 * Crud interface using Zend_Db_Table
 *
 * @author     Mon Zafra <monzee at gmail>
 * @copyright  (c)2009 Mon Zafra
 * @category   Mz
 * @package    modules
 * @subpackage crud
 * @license    http://mz-project.googlecode.com/svn/trunk/LICENSE    MIT License
 * @version    SVN: $Id: IndexController.php 27 2009-05-09 18:26:19Z monzee $
 */
class Crud_IndexController extends Zend_Controller_Action
{
    /**
     * @var Zend_Db_Adapter_Abstract
     */
    protected $_db;
    /**
     * @var Zend_Db_Table_Abstract
     */
    protected $_table;

    /**
     * Setup table
     */
    public function init()
    {
        $bootstraps = $this->getInvokeArg('bootstrap')->getResource('modules');
        $module = $this->_request->getModuleName();
        $this->_db = $bootstraps[$module]->getAdapter();
        if (null === $this->_db) {
            throw new Zend_Db_Table_Exception('[CRUD] Zend_Db_Table default adapter not set!');
        }

        if ($this->_hasParam('table')) {
            $tableName = $this->_getParam('table');
            $this->_table = new Crud_Model_Table(array(
                Zend_Db_Table_Abstract::ADAPTER => $this->_db,
                Zend_Db_Table_Abstract::NAME => $tableName,
            ));
        }

        $this->_helper->redirector->setPrependBase(false);
    }

    /**
     * Pass flash messages to view
     */
    public function postDispatch()
    {
        $flash = $this->getHelper('FlashMessenger');
        $flash->setNamespace('crud');
        /* @var $flash Zend_Controller_Action_Helper_FlashMessenger */
        if ($flash->hasMessages()) {
            $this->view->messages = $flash->getMessages();
        } else if ($flash->hasCurrentMessages()) {
            $this->view->messages = $flash->getCurrentMessages();
            $flash->clearCurrentMessages();
        }
    }

    /**
     * Front
     *
     * @return void
     */
    public function indexAction()
    {
        if ($this->_hasParam('table') && '' != $this->_getParam('table')) {
            $this->_forward('list');
            return;
        }

        /* TODO:
         * x allow user to specify a custom DSN, store DSN in session
         * x allow user to execute custom sql
         * - or table creation interface instead of/in addition to the above?
         * - recover from Zend_Db exceptions
         * - new/edit: add a sidebar for looking up values from other tables
         * - new/edit: add option to execute commands/filters on the entered data
         *   + md5
         *   + current timestamp
         *   + set to null (if nullable)
         *   + current date/time in proper format
         * - show/new/edit: links to next/previous records
         * - new/edit: field validators per datatype
         * - list: number of entries per page
         */

        $adapterForm = $this->_hasParam('adapterForm')
            ? $this->_getParam('adapterForm') : new Crud_Form_DbAdapter();
        $queryForm = $this->_hasParam('queryForm')
            ? $this->_getParam('queryForm') : new Crud_Form_DbQuery();

        $session = new Zend_Session_Namespace('crud');

        $curAdapter = isset($session->adapter) ? $session->adapter : 'config';
        switch ($curAdapter) {
            case 'config':
                $this->view->curAdapter = '<tt>crud/config.ini</tt> settings.';
                break;
            case 'default':
                $this->view->curAdapter = 'the default Zend_Db_Table adapter.';
                break;
            case 'custom':
                $this->view->curAdapter = 'these settings:';
                break;
            default:
                break;
        }

        if (isset($session->adapterParams)) {
            $adapterForm->populate($session->adapterParams);
        }

        try {
            $tables = $this->_db->listTables();
        } catch (Exception $e) {
            $message = $e->getMessage();
            $this->_helper->flashMessenger
                ->setNamespace('crud')
                ->addMessage('An error occurred while connecting to the database. Please check your config.')
                ->addMessage($message);
            $tables = array();
        }

        $this->view->tables = $tables;
        $this->view->dbForm = $adapterForm->setAction($this->view->url(array('action' => 'set-adapter')));
        $this->view->queryForm = $queryForm->setAction($this->view->url(array('action' => 'exec')));
    }

    /**
     * Changes the Zend_Db adapter to be used.
     *
     * @return void
     */
    public function setAdapterAction()
    {
        $req = $this->getRequest();
        if ($req->isPost()) {
            $session = new Zend_Session_Namespace('crud', true);
            if ($req->getPost('useConfig', false)) {
                unset($session->adapter);
            } else if ($req->getPost('useDefault', false)) {
                $session->adapter = 'default';
            } else {
                $form = new Crud_Form_DbAdapter();
                if (!$form->isValid($req->getPost())) {
                    $this->_setParam('adapterForm', $form);
                    $this->_forward('index');
                    return;
                }

                $session->adapter = 'custom';
                $params = $form->getValues();
                if (empty($params['port'])) {
                    unset($params['port']);
                }
                $session->adapterParams = $params;
            }
        }

        $this->_redirect($this->view->url(array('action' =>'index')));
    }

    /**
     * Executes custom SQL
     *
     * @todo this.
     */
    public function execAction()
    {

        $req = $this->getRequest();
        if ($req->isPost()) {
            $form = new Crud_Form_DbQuery();
            $valid = $form->isValid($req->getPost());
            $this->_setParam('queryForm', $form);

            if (!$valid) {
                $this->_forward('index');
                return;
            }

            $flash = $this->_helper->flashMessenger->setNamespace('crud');
            try {
                $sql = $form->getValue('query');
                $this->_db->beginTransaction();
                $this->_db->query($sql);
                $this->_db->commit();
                $flash->addMessage('Your query was successfully executed.');
            } catch (Zend_Db_Statement_Exception $e) {
                $flash->addMessage('An error occurred while executing your query.')
                      ->addMessage($e->getMessage())
                      ->addMessage('Query was: <br /><pre>' . nl2br($sql) . '</pre>');
                $this->_db->rollBack();
                $this->_forward('index');
                return;
            }
        }


        $this->_redirect($this->view->url(array('action' =>'index')));
    }

    /**
     * Lists the contents of the table.
     *
     * @return void
     */
    public function listAction()
    {
        if (null === $table = $this->_table) {
            $this->_setParam('table', null);
            $this->_forward('index');
            return;
        }
        $tableName = $table->getTableName();

        $sel = $this->_db->select()->from($tableName, '*');
        if ($this->_hasParam('sort')) {
            $ord = $this->_hasParam('order')
                 ? strtoupper($this->_getParam('order'))
                 : 'ASC';
            $sel->order($this->_getParam('sort') . ' ' . $ord);
            $this->view->sort = $this->_getParam('sort');
        }
        $paginator = Zend_Paginator::factory($sel);
        $paginator->setCurrentPageNumber($this->_getParam('page', 1))
                  ->setItemCountPerPage($this->_getParam('perpage', 10));

        $this->view->table = $tableName;
        $this->view->headers = $table->getColumns();
        $this->view->primary = $table->getPrimaryKeys();
        $this->view->data = $paginator;
    }

    /**
     * Adds a row.
     *
     * @return void
     */
    public function createAction()
    {
        if (null === $table = $this->_table) {
            $this->_setParam('table', null);
            $this->_forward('index');
            return;
        }
        $req = $this->getRequest();
        $form = new Crud_Form_Table($table);

        if ($req->isPost()) {
            if ($form->isValid($req->getPost())) {
                if (!empty($form->password)) {
                    $form->password->setValue(md5($form->getValue('password')));
                }
                $new = $table->createRow();
                $values = $form->getValues();
                $saved = $this->_save($new, $values);

                if ($saved) {
                    $this->_helper->flashMessenger
                         ->setNamespace('crud')
                         ->addMessage('New record added.');
                }

                $this->_redirect($this->view->urlTo('crud')->action('create')->table($table->getTableName()));
                return;
            } else {
                $this->_helper->flashMessenger
                     ->setNamespace('error')
                     ->addMessage('There were some problems with your request.');
            }
        }

        if ($date = $form->getElement('created_at')) {
            $date->setValue(Zend_Date::now()->toString('YYYY-MM-dd HH:mm:ss'));
        }
        $this->view->table = $table->getTableName();
        $this->view->form = $form;
    }

    /**
     * Displays one row.
     *
     * @return void
     */
    public function readAction()
    {
        if (null === $table = $this->_table) {
            $this->_setParam('table', null);
            $this->_forward('index');
            return;
        }
        $req = $this->getRequest();

        if ($req->isPost()) {
            if ($req->getPost('edit')) {
                $this->_redirect($this->_helper->url->url(array('action' => 'update')));
            } else if ($req->getPost('delete')) {
                $this->_redirect($this->_helper->url->url(array('action' => 'delete')));
            }
            return;
        }

        $params = $req->getUserParams();
        foreach ($params as &$value) {
            $value = urldecode($value);
        }
        $this->view->table = $table->getTableName();
        $this->view->data = $table->getRow($params)->toArray();
    }

    /**
     * Edits a row.
     *
     * @return void
     */
    public function updateAction()
    {
        if (null === $table = $this->_table) {
            $this->_setParam('table', null);
            $this->_forward('index');
            return;
        }
        $tableName = $table->getTableName();
        $req = $this->getRequest();
        $params = $req->getUserParams();
        foreach ($params as &$value) {
            $value = urldecode($value);
        }

        $form = new Crud_Form_Table($table);
        $row = $table->getRow($params);

        if (!$req->isPost()) {
            $form->populate($row->toArray());
        } else {
            if ($form->isValid($req->getPost())) {
                if (!empty($form->password)) {
                    $val = $form->getValue('password');
                    $val = !empty($val) ? md5($val) : null;
                    $form->password->setValue($val);
                }
                $values = $form->getValues();

                $saved = $this->_save($row, $values);
                if ($saved) {
                    $this->_helper->flashMessenger
                         ->setNamespace('crud')
                         ->addMessage('Record updated.');
                }

                $this->_redirect($this->view->urlTo('crud')->action('list')->table($tableName));
                return;
            }
        }

        if (($date = $form->getElement('updated_at')) && null === $date->getValue()) {
            $date->setValue(Zend_Date::now()->toString('YYYY-MM-dd HH:mm:ss'));
        }
        $this->view->table = $tableName;
        $this->view->form = $form;
    }

    /**
     * Deletes a row.
     *
     * @return void
     */
    public function deleteAction()
    {
        if (null === $table = $this->_table) {
            $this->_setParam('table', null);
            $this->_forward('index');
            return;
        }
        $tableName = $table->getTableName();
        $req = $this->getRequest();
        $params = $req->getUserParams();
        foreach ($params as &$value) {
            $value = urldecode($value);
        }

        $row = $table->getRow($params);
        if ($req->isPost()) {
            if ($req->getPost('yes')) {
                $row->delete();
                $this->_helper->flashMessenger
                     ->setNameSpace('crud')
                     ->addMessage('Record deleted.');
            } else {
                $this->_helper->flashMessenger
                     ->setNameSpace('crud')
                     ->addMessage('Delete cancelled.');
            }

            $this->_redirect($this->view->urlTo('crud')->action('list')->table($tableName));
            return;
        }

        $this->view->table = $tableName;
        $this->view->data = $row->toArray();
        $this->view->pk = $table->getPrimaryKeys();
    }

    /**
     * save a row, catch any exception
     *
     * @param Zend_Db_Table_Row_Abstract $row
     * @param array $values
     */
    protected function _save($row, $values)
    {
        foreach ($values as $key => $val) {
            if (null === $val) {
                unset($values[$key]);
            } else if ('' == $val) {
                $values[$key] = null;
            }
        }

        try {
            $profiler = $this->_db->getProfiler()->setEnabled(true);
            $row->setFromArray($values)
                ->save();
        } catch (Zend_Db_Statement_Exception $e) {
            $query = $profiler->getLastQueryProfile();
            $flash = $this->_helper->flashMessenger;
            $flash->setNamespace('crud')
                  ->addMessage('Save failed.')
                  ->addMessage($e->getMessage());

            if (false !== $query) {
                $flash->addMessage('Query was: <strong>' . $query->getQuery() . '</strong>')
                      ->addMessage('Values were: <strong>' .
                print_r($query->getQueryParams(), true) . '</strong>');
            }
            return false;
        } catch (Zend_Db_Table_Row_Exception $e) {
            $flash = $this->_helper->flashMessenger;
            $flash->setNamespace('crud')
                  ->addMessage('An error occurred while saving. The data may or
                    may not have been saved.')
                  ->addMessage('<strong>' . get_class($e) . '</strong>: ' . $e->getMessage());
            return false;
        }

        return true;
    }

}
